package org.dromara.workflow.utils;

import cn.hutool.core.bean.BeanUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.workflow.domain.vo.TaskVo;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.DeploymentQuery;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;

import java.util.Collection;
import java.util.List;
import java.util.Set;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：查询工具
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueryUtils {

    /**
     * ProcessEngine的常量定义
     * 使用SpringUtils工具类获取ProcessEngine实例
     * 这里使用常量的形式保存ProcessEngine实例，是为了在类内部可以直接使用，避免重复创建实例的开销
     */
    private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);

    /**
     * 创建并返回一个模型查询对象
     *
     * 本方法用于初始化一个模型查询对象，以便进行模型的相关查询操作如果启用了租户隔离，
     * 则会设置查询的租户ID，确保只能查询到属于当前租户的模型
     *
     * @return ModelQuery 初始化后的模型查询对象
     */
    public static ModelQuery modelQuery() {
        // 获取流程引擎的仓库服务，并创建模型查询对象
        ModelQuery query = PROCESS_ENGINE.getRepositoryService().createModelQuery();
        // 如果启用了租户隔离
        if (TenantHelper.isEnable()) {
            // 设置模型查询的租户ID，实现租户隔离的查询
            query.modelTenantId(TenantHelper.getTenantId());
        }
        // 返回初始化后的模型查询对象
        return query;
    }

    /**
     * 创建一个流程定义查询对象并根据需要设置租户ID查询条件
     *
     * @return ProcessDefinitionQuery 返回一个可能已经配置了租户ID查询条件的流程定义查询对象
     */
    public static ProcessDefinitionQuery definitionQuery() {
        // 创建流程定义查询对象
        ProcessDefinitionQuery query = PROCESS_ENGINE.getRepositoryService().createProcessDefinitionQuery();
        // 如果启用了租户支持，则设置查询的租户ID
        if (TenantHelper.isEnable()) {
            query.processDefinitionTenantId(TenantHelper.getTenantId());
        }
        // 返回查询对象
        return query;
    }

    /**
     * 创建部署查询对象，并根据租户助手的配置设置租户ID过滤条件
     *
     * @return DeploymentQuery 部署查询对象，用于执行部署的查询操作
     */
    public static DeploymentQuery deploymentQuery() {
        // 创建部署查询对象
        DeploymentQuery query = PROCESS_ENGINE.getRepositoryService().createDeploymentQuery();
        // 检查是否启用了租户隔离
        if (TenantHelper.isEnable()) {
            // 设置查询的租户ID，实现租户隔离
            query.deploymentTenantId(TenantHelper.getTenantId());
        }
        // 返回构建的查询对象
        return query;
    }

    /**
     * 根据部署ID查询部署信息
     * 此方法用于在部署查询中指定特定的部署ID，以获取相关的部署信息
     *
     * @param deploymentId 部署的唯一标识符，用于指定要查询的部署
     * @return 返回一个部署查询对象，该对象已根据提供的部署ID进行过滤
     */
    public static DeploymentQuery deploymentQuery(String deploymentId) {
        return deploymentQuery().deploymentId(deploymentId);
    }

    /**
     * 根据部署ID列表创建部署查询
     * 此方法用于初始化部署查询对象，并设置指定的部署ID列表作为查询条件
     *
     * @param deploymentIds 部署ID列表，用于在查询中作为筛选条件
     * @return 返回设置了部署ID列表的DeploymentQuery对象，以便进行进一步的查询操作
     */
    public static DeploymentQuery deploymentQuery(List<String> deploymentIds) {
        return deploymentQuery().deploymentIds(deploymentIds);
    }

    /**
     * 创建历史任务实例查询对象，并根据租户属性设置查询条件
     *
     * @return HistoricTaskInstanceQuery 历史任务实例查询对象，用于执行复杂查询
     */
    public static HistoricTaskInstanceQuery hisTaskInstanceQuery() {
        // 从流程引擎中获取历史服务，并创建历史任务实例查询对象
        HistoricTaskInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricTaskInstanceQuery();

        // 如果启用了租户属性，则在查询中设置租户ID
        if (TenantHelper.isEnable()) {
            query.taskTenantId(TenantHelper.getTenantId());
        }

        // 返回构建好的查询对象
        return query;
    }

    /**
     * 根据流程实例ID创建历史任务实例查询
     * 此方法用于初始化一个历史任务实例的查询，通过指定流程实例ID，可以对相关的历史任务实例进行查询操作
     *
     * @param processInstanceId 流程实例ID，用于关联查询的历史任务实例
     * @return 返回历史任务实例查询对象，通过该对象可以执行进一步的查询操作
     */
    public static HistoricTaskInstanceQuery hisTaskInstanceQuery(String processInstanceId) {
        return hisTaskInstanceQuery().processInstanceId(processInstanceId);
    }

    /**
     * 使用业务键查询历史任务实例
     *
     * @param businessKey 业务键，用于定位特定业务流程的实例
     * @return 返回一个 HistoricTaskInstanceQuery 对象，该对象允许进一步查询与指定业务键关联的历史任务实例
     *
     * 此方法提供了一种简便方式来基于业务键进行历史任务实例的查询，避免了直接操作查询对象的复杂性
     */
    public static HistoricTaskInstanceQuery hisTaskBusinessKeyQuery(String businessKey) {
        return hisTaskInstanceQuery().processInstanceBusinessKey(businessKey);
    }

    /**
     * 创建一个流程实例查询对象，并根据需要设置租户ID查询条件
     *
     * @return ProcessInstanceQuery 返回流程实例查询对象
     */
    public static ProcessInstanceQuery instanceQuery() {
        // 从流程引擎中获取运行时服务，并创建流程实例查询对象
        ProcessInstanceQuery query = PROCESS_ENGINE.getRuntimeService().createProcessInstanceQuery();
        // 如果启用租户功能，则设置查询条件为指定的租户ID
        if (TenantHelper.isEnable()) {
            query.processInstanceTenantId(TenantHelper.getTenantId());
        }
        // 返回构建好的查询对象
        return query;
    }

    /**
     * 根据流程实例ID创建流程实例查询对象
     * 通过这个方法，用户可以启动一个针对特定流程实例的查询，以便于进一步细化查询条件
     *
     * @param processInstanceId 流程实例ID，用于标识唯一的流程实例
     * @return 返回一个流程实例查询对象，允许进一步链式调用以细化查询条件
     */
    public static ProcessInstanceQuery instanceQuery(String processInstanceId) {
        return instanceQuery().processInstanceId(processInstanceId);
    }

    /**
     * 根据业务键查询流程实例
     *
     * @param businessKey 用于查询的业务键
     * @return 返回一个流程实例查询对象，该对象根据指定的业务键进行了过滤
     */
    public static ProcessInstanceQuery businessKeyQuery(String businessKey) {
        // 使用指定的业务键创建流程实例查询
        return instanceQuery().processInstanceBusinessKey(businessKey);
    }

    /**
     * 根据流程实例ID集合创建流程实例查询
     * <p>
     * 此方法用于初始化一个流程实例查询，通过传递流程实例ID集合来筛选出特定的流程实例
     * 它主要用于流程引擎的API调用，以便用户能够基于特定的ID集合进行流程实例的查询和操作
     *
     * @param processInstanceIds 流程实例ID集合，用于指定需要查询的流程实例
     * @return 返回一个流程实例查询对象，该对象已经设置了根据流程实例ID的查询条件
     */
    public static ProcessInstanceQuery instanceQuery(Set<String> processInstanceIds) {
        return instanceQuery().processInstanceIds(processInstanceIds);
    }

    /**
     * 创建历史流程实例查询对象并根据租户配置设置查询条件
     *
     * @return HistoricProcessInstanceQuery 历史流程实例查询对象，用于执行复杂查询
     */
    public static HistoricProcessInstanceQuery hisInstanceQuery() {
        // 从流程引擎中获取历史服务
        HistoricProcessInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricProcessInstanceQuery();
        // 如果租户功能已启用，则根据租户ID过滤查询
        if (TenantHelper.isEnable()) {
            query.processInstanceTenantId(TenantHelper.getTenantId());
        }
        return query;
    }

    /**
     * 根据流程实例ID创建历史流程实例查询对象
     * 通过这个方法，可以获取到特定流程实例的历史信息，便于进行流程历史的管理和分析
     *
     * @param processInstanceId 流程实例ID，用于指定查询哪个流程实例的历史信息
     * @return 返回一个历史流程实例查询对象，用于执行实际的查询操作
     */
    public static HistoricProcessInstanceQuery hisInstanceQuery(String processInstanceId) {
        return hisInstanceQuery().processInstanceId(processInstanceId);
    }

    /**
     * 根据业务键查询历史流程实例
     *
     * @param businessKey 业务键，用于查询对应业务键的历史流程实例
     * @return 返回历史流程实例查询对象，以便进一步执行查询操作
     */
    public static HistoricProcessInstanceQuery hisBusinessKeyQuery(String businessKey) {
        return hisInstanceQuery().processInstanceBusinessKey(businessKey);
    }

    /**
     * 根据流程实例ID集合创建历史流程实例查询
     *
     * @param processInstanceIds 流程实例ID集合，用于筛选查询结果
     * @return 返回一个历史流程实例查询对象，可用于进一步的查询操作
     */
    public static HistoricProcessInstanceQuery hisInstanceQuery(Set<String> processInstanceIds) {
        return hisInstanceQuery().processInstanceIds(processInstanceIds);
    }

    /**
     * 创建历史活动实例查询对象，并根据租户配置设置查询条件
     *
     * @return HistoricActivityInstanceQuery 返回历史活动实例查询对象，用于执行复杂查询
     */
    public static HistoricActivityInstanceQuery hisActivityInstanceQuery() {
        // 获取历史服务中的历史活动实例查询对象
        HistoricActivityInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricActivityInstanceQuery();
        // 如果启用了租户隔离，则将租户ID作为查询条件添加到查询对象中
        if (TenantHelper.isEnable()) {
            query.activityTenantId(TenantHelper.getTenantId());
        }
        // 返回构建好的查询对象
        return query;
    }

    /**
     * 根据流程实例ID创建历史活动实例查询
     * 此方法用于初始化一个历史活动实例的查询，通过指定流程实例ID，
     * 可以对历史活动实例进行过滤和查询这使得流程引擎能够针对特定的流程实例，
     * 返回相关的历史活动信息，用于流程管理或者审计目的
     *
     * @param processInstanceId 流程实例ID，用于指定要查询的历史活动所属的流程实例
     * @return 返回历史活动实例查询对象，可以通过该对象进一步指定查询条件
     */
    public static HistoricActivityInstanceQuery hisActivityInstanceQuery(String processInstanceId) {
        return hisActivityInstanceQuery().processInstanceId(processInstanceId);
    }

    /**
     * 创建并返回一个任务查询对象，该对象可用于查询任务
     * 此方法中，考虑到租户身份的可能影响，如果启用了租户助手，则在查询中添加租户ID条件
     *
     * @return TaskQuery 返回一个任务查询对象，可能根据租户ID进行了筛选
     */
    public static TaskQuery taskQuery() {
        // 从流程引擎中获取任务服务，并创建任务查询对象
        TaskQuery query = PROCESS_ENGINE.getTaskService().createTaskQuery();
        // 检查是否启用了租户功能，如果启用，则设置查询的租户ID
        if (TenantHelper.isEnable()) {
            query.taskTenantId(TenantHelper.getTenantId());
        }
        // 返回构建好的任务查询对象
        return query;
    }

    /**
     * 根据流程实例ID创建任务查询
     *
     * @param processInstanceId 流程实例ID，用于关联任务和流程实例
     * @return 返回任务查询对象，调用者可以通过该对象进一步定制任务查询条件或执行查询
     */
    public static TaskQuery taskQuery(String processInstanceId) {
        return taskQuery().processInstanceId(processInstanceId);
    }

    /**
     * 根据流程实例ID集合查询任务
     *
     * @param processInstanceIds 流程实例ID集合，用于查询关联的任务
     * @return 返回任务查询对象，用于执行查询操作
     */
    public static TaskQuery taskQuery(Collection<String> processInstanceIds) {
        return taskQuery().processInstanceIdIn(processInstanceIds);
    }

    /**
     * 按照任务id查询当前任务
     *
     * @param taskId 任务id
     * @return 包含任务信息的TaskVo对象，若任务不存在则返回null
     */
    public static TaskVo getTask(String taskId) {
        // 根据任务ID查询任务对象
        Task task = PROCESS_ENGINE.getTaskService().createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            // 若任务不存在，返回null
            return null;
        }

        // 查询流程实例
        ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();

        // 将Task对象转换为TaskVo对象
        TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
        // 设置业务标识
        taskVo.setBusinessKey(processInstance.getBusinessKey());
        // 判断任务是否为多实例任务
        taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);

        // 获取业务状态
        String businessStatus = WorkflowUtils.getBusinessStatus(taskVo.getBusinessKey());
        taskVo.setBusinessStatus(businessStatus);

        // 返回填充完整的TaskVo对象
        return taskVo;
    }
}
